package com.jeecg.p3.base.util;

import org.dom4j.*;
import org.jeecgframework.p3.core.common.utils.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.MessageDigest;
import java.util.*;

/**
 * @author dell
 * @ClassName PayUtils
 * @Description 支付工具类
 * @Date 2019/11/18 20:17
 * @Version 1.0
 */
public class PayUtils {
    private static final Logger log = LoggerFactory.getLogger(PayUtils.class);

    /**
     * 签名
     *
     * @param paramMap
     * @param key
     * @return
     */
    public static String getSign(SortedMap<String, Object> paramMap, String key) {
        StringBuilder signBuilder = new StringBuilder();
        for (Map.Entry<String, Object> entry : paramMap.entrySet()) {
            if (!"sign".equals(entry.getKey()) && !StringUtil.isEmpty(entry.getValue())) {
                signBuilder.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
            }
        }
        signBuilder.append("key=").append(key);
        log.info("微信待签名参数字符串:{}", signBuilder.toString());
        return MD5Util.encode(signBuilder.toString()).toUpperCase();
    }

    /**
     * 转xml格式
     *
     * @param paramMap
     * @return
     */
    public static String mapToXml(SortedMap<String, Object> paramMap) {
        StringBuilder dataBuilder = new StringBuilder("<xml>");
        for (Map.Entry<String, Object> entry : paramMap.entrySet()) {
            if (!StringUtil.isEmpty(entry.getValue())) {
                dataBuilder.append("<").append(entry.getKey()).append(">").append(entry.getValue()).append("</").append(entry.getKey()).append(">");
            }
        }
        dataBuilder.append("</xml>");
        log.info("Map转Xml结果:{}", dataBuilder.toString());
        return dataBuilder.toString();
    }

    /**
     * 解析xml为map
     * @param xmlStr
     * @return
     */
    public static Map<String, String> xmlToMap(String xmlStr){
        List<Map<String, String>> resultList = iterateWholeXML(xmlStr);
        Map<String, String> retMap = new HashMap<String,String>();
        for (int i = 0; i < resultList.size(); i++) {
            Map map = (Map) resultList.get(i);

            for (Iterator iterator = map.keySet().iterator(); iterator.hasNext();) {
                String key = (String) iterator.next();
                retMap.put(key, (String) map.get(key));

            }
        }
        return retMap;
    }
    private static List<Map<String, String>> iterateWholeXML(String xmlStr) {

        List<Map<String, String>> list = new ArrayList<Map<String, String>>();

        try {
            Document document = DocumentHelper.parseText(xmlStr);
            Element root = document.getRootElement();
            recursiveNode(root, list);
            return list;
        } catch (DocumentException e) {
            e.printStackTrace();
        }
        return null;
    }
    private static void recursiveNode(Element root, List<Map<String, String>> list) {

        // 遍历根结点的所有孩子节点
        HashMap<String, String> map = new HashMap<String, String>();
        for (Iterator iter = root.elementIterator(); iter.hasNext();) {
            Element element = (Element) iter.next();
            if (element == null) {
                continue;
            }
            // 获取属性和它的值
            for (Iterator attrs = element.attributeIterator(); attrs.hasNext();) {
                Attribute attr = (Attribute) attrs.next();
                System.out.println(attr);
                if (attr == null) {
                    continue;
                }
                String attrName = attr.getName();
                System.out.println("attrname" + attrName);
                String attrValue = attr.getValue();

                map.put(attrName, attrValue);
            }
            // 如果有PCDATA，则直接提出
            if (element.isTextOnly()) {
                String innerName = element.getName();
                String innerValue = element.getText();

                map.put(innerName, innerValue);
                list.add(map);
            } else {
                // 递归调用
                recursiveNode(element, list);
            }
        }
    }

    /**
     * 微信回调参数解析
     * @param request
     * @return
     */
    public static String getPostStr(HttpServletRequest request){
        StringBuffer sb = new StringBuffer();
        try {
            InputStream is = request.getInputStream();
            InputStreamReader isr = new InputStreamReader(is, "UTF-8");
            BufferedReader br = new BufferedReader(isr);
            String s = "";

            while ((s = br.readLine()) != null) {
                sb.append(s);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        String xml = sb.toString(); //即为接收到微信端发送过来的xml数据
        log.info(xml+"========================");
        return xml;
    }


    /**
     * 判断支付结果通知中的sign是否有效
     * @param reqData 向wxpay post的请求数据
     * @return 签名是否有效
     * @throws Exception
     */
    public static boolean isPayResultNotifySignatureValid(Map<String, String> reqData) throws Exception {
        String signTypeInData = reqData.get(WXPayConstants.FIELD_SIGN_TYPE);
        WXPayConstants.SignType signType;
        if (signTypeInData == null) {
            signType = WXPayConstants.SignType.MD5;
        }
        else {
            signTypeInData = signTypeInData.trim();
            if (signTypeInData.length() == 0) {
                signType = WXPayConstants.SignType.MD5;
            }
            else if (WXPayConstants.MD5.equals(signTypeInData)) {
                signType = WXPayConstants.SignType.MD5;
            }
            else if (WXPayConstants.HMACSHA256.equals(signTypeInData)) {
                signType = WXPayConstants.SignType.HMACSHA256;
            }
            else {
                throw new Exception(String.format("Unsupported sign_type: %s", signTypeInData));
            }
        }
        return  isSignatureValid(reqData, WeixinConfigUtil.partnerKey, signType);
    }

    /**
     * 判断签名是否正确，必须包含sign字段，否则返回false。
     *
     * @param data Map类型数据
     * @param key API密钥
     * @param signType 签名方式
     * @return 签名是否正确
     * @throws Exception
     */
    public static boolean isSignatureValid(Map<String, String> data, String key, WXPayConstants.SignType signType) throws Exception {
        if (!data.containsKey(WXPayConstants.FIELD_SIGN) ) {
            return false;
        }
        String sign = data.get(WXPayConstants.FIELD_SIGN);
        return generateSignature(data, key, signType).equals(sign);
    }

    /**
     * 生成签名. 注意，若含有sign_type字段，必须和signType参数保持一致。
     *
     * @param data 待签名数据
     * @param key API密钥
     * @param signType 签名方式
     * @return 签名
     */
    public static String generateSignature(final Map<String, String> data, String key, WXPayConstants.SignType signType) throws Exception {
        Set<String> keySet = data.keySet();
        String[] keyArray = keySet.toArray(new String[keySet.size()]);
        Arrays.sort(keyArray);
        StringBuilder sb = new StringBuilder();
        for (String k : keyArray) {
            if (k.equals(WXPayConstants.FIELD_SIGN)) {
                continue;
            }
            if (data.get(k).trim().length() > 0) // 参数值为空，则不参与签名
                sb.append(k).append("=").append(data.get(k).trim()).append("&");
        }
        sb.append("key=").append(key);
        if (WXPayConstants.SignType.MD5.equals(signType)) {
            return MD5(sb.toString()).toUpperCase();
        }
        else if (WXPayConstants.SignType.HMACSHA256.equals(signType)) {
            return HMACSHA256(sb.toString(), key);
        }
        else {
            throw new Exception(String.format("Invalid sign_type: %s", signType));
        }
    }

    /**
     * 生成 HMACSHA256
     * @param data 待处理数据
     * @param key 密钥
     * @return 加密结果
     * @throws Exception
     */
    public static String HMACSHA256(String data, String key) throws Exception {
        Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
        SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
        sha256_HMAC.init(secret_key);
        byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
        StringBuilder sb = new StringBuilder();
        for (byte item : array) {
            sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
        }
        return sb.toString().toUpperCase();
    }

    /**
     * 生成 MD5
     *
     * @param data 待处理数据
     * @return MD5结果
     */
    public static String MD5(String data) throws Exception {
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] array = md.digest(data.getBytes("UTF-8"));
        StringBuilder sb = new StringBuilder();
        for (byte item : array) {
            sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
        }
        return sb.toString().toUpperCase();
    }

    /**
     * 生成随机字符串
     *
     * @return
     */
    public static String getnonceStr() {
        Random random = new Random();
        StringBuilder nonceStrBuilder = new StringBuilder();
        for (int i = 0; i < 31; i++) {
            nonceStrBuilder.append(random.nextInt(10));
        }
        return nonceStrBuilder.toString();
    }



}
